/**
 * 
 */
package ua.kiev.ntukpi.asu.ga.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

import ua.kiev.ntukpi.asu.ga.Individual;
import ua.kiev.ntukpi.asu.ga.Job;
import ua.kiev.ntukpi.asu.ga.interfaces.Crossover;

/**
 * @author Alexander Galibey
 *
 */
public class CrossoverOneDot implements Crossover {

	private static final String NAME = "One Dot Crossover";


	public List<Individual> cross(List<Individual> selection, double factor) {
		
		if (selection.size() != 2){
			
			throw new IllegalArgumentException("Amount of parent must be equal two.");
		}
		Individual firstParent = selection.get(0);
		Individual secondParent = selection.get(1);
		Individual firstChild = getChild(firstParent, secondParent, factor);
		Individual secondChild = getChild(secondParent, firstParent, factor);
		List<Individual> result = new ArrayList<Individual>();
		result.add(firstChild);
		result.add(secondChild);
		
		return result;
	}
	
	private Individual getChild(Individual firstParent, Individual secondParent, double l){
		
		Individual child = new Individual();
		List<Job> firstJobs = firstParent.getJobs();
		List<Job> secondJobs = secondParent.getJobs();
		int delimiter = (int) (firstJobs.size() * l);
		List<Job> childJobs = new ArrayList<Job>(firstJobs.size());
		List<Job> sublistFromFirstParent = firstJobs.subList(0, delimiter);
		List<Job> sublistFromSecondParent = secondJobs.subList(delimiter, secondJobs.size());
		childJobs.addAll(sublistFromFirstParent);
		
		HashSet<Job> wholeCollectionJobs = new HashSet<Job>(firstJobs);
		HashSet<Job> alreadyAddedJobs = new HashSet<Job>(sublistFromFirstParent);
		
		List<Integer> missedIndexes = new ArrayList<Integer>();
		for(int i = 0; i < sublistFromSecondParent.size(); i++){
			
			Job job = sublistFromSecondParent.get(i);
			childJobs.add(job);
			if (alreadyAddedJobs.contains(job)){
				
				missedIndexes.add(childJobs.lastIndexOf(job));
			}
			alreadyAddedJobs.add(job);
		}
		wholeCollectionJobs.removeAll(alreadyAddedJobs);
		List<Job> remained = new ArrayList<Job>(wholeCollectionJobs);
		Collections.shuffle(remained);
		int remainedIndex = 0;
		for(Integer index : missedIndexes){
			
			childJobs.set(index, remained.get(remainedIndex));
			remainedIndex++;
		}
		child.setJobs(childJobs);
		return child;
	}

	
	public String getName() {
		
		return NAME;
	}

}
